#!/bin/bash
# /usr/lib/check_mk_agent/local/300/check_npm

set -u

CONFIG="/etc/check_mk/npm.conf"

OK=0
WARN=1
CRIT=2
UNKNOWN=3

#
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
#

if [[ ! -r "$CONFIG" ]]; then
    printf '%s\n' \
        "${UNKNOWN} \"Nginx Proxy Manager\" - Configuration file ${CONFIG} missing or unreadable"
    exit 0
fi

# shellcheck disable=SC1090
source "$CONFIG"

NPM_URL="${NPM_URL:-http://127.0.0.1:81}"
NPM_IDENTITY="${NPM_IDENTITY:-}"
NPM_SECRET="${NPM_SECRET:-}"

CHECK_UPDATES="${CHECK_UPDATES:-yes}"
NPM_RELEASE_API="${NPM_RELEASE_API:-https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest}"

#
# ---------------------------------------------------------------------------
# Requirements
# ---------------------------------------------------------------------------
#

if ! command -v curl >/dev/null 2>&1; then
    printf '%s\n' \
        "${UNKNOWN} \"Nginx Proxy Manager\" - curl is not installed"
    exit 0
fi

if ! command -v jq >/dev/null 2>&1; then
    printf '%s\n' \
        "${UNKNOWN} \"Nginx Proxy Manager\" - jq is not installed"
    exit 0
fi

if [[ -z "$NPM_IDENTITY" || -z "$NPM_SECRET" ]]; then
    printf '%s\n' \
        "${UNKNOWN} \"Nginx Proxy Manager\" - NPM_IDENTITY or NPM_SECRET is not configured"
    exit 0
fi

#
# ---------------------------------------------------------------------------
# curl configuration
# ---------------------------------------------------------------------------
#

CURL_OPTS=(
    --silent
    --show-error
    --fail
    --connect-timeout 3
    --max-time 10
)

#
# ---------------------------------------------------------------------------
# Public API health / installed version
# ---------------------------------------------------------------------------
#

STATUS_RESPONSE="$(
    curl \
        "${CURL_OPTS[@]}" \
        "${NPM_URL%/}/api/" \
        2>/dev/null
)"

STATUS_RC=$?

if [[ $STATUS_RC -ne 0 || -z "$STATUS_RESPONSE" ]]; then
    printf '%s\n' \
        "${CRIT} \"Nginx Proxy Manager\" - API unavailable at ${NPM_URL}"
    exit 0
fi

if ! printf '%s' "$STATUS_RESPONSE" |
    jq -e 'type == "object"' >/dev/null 2>&1
then
    printf '%s\n' \
        "${UNKNOWN} \"Nginx Proxy Manager\" - Public API returned unexpected data"
    exit 0
fi

API_STATUS="$(
    printf '%s' "$STATUS_RESPONSE" |
        jq -r '.status // empty'
)"

SETUP_COMPLETE="$(
    printf '%s' "$STATUS_RESPONSE" |
        jq -r '.setup // false'
)"

VERSION_MAJOR="$(
    printf '%s' "$STATUS_RESPONSE" |
        jq -r '.version.major // empty'
)"

VERSION_MINOR="$(
    printf '%s' "$STATUS_RESPONSE" |
        jq -r '.version.minor // empty'
)"

VERSION_REVISION="$(
    printf '%s' "$STATUS_RESPONSE" |
        jq -r '.version.revision // empty'
)"

INSTALLED_VERSION=""

if [[ -n "$VERSION_MAJOR" && -n "$VERSION_MINOR" && -n "$VERSION_REVISION" ]]; then
    INSTALLED_VERSION="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}"
fi

if [[ "$API_STATUS" != "OK" ]]; then
    printf '%s\n' \
        "${CRIT} \"Nginx Proxy Manager\" - API responded with status ${API_STATUS:-unknown}"
    exit 0
fi

if [[ "$SETUP_COMPLETE" != "true" ]]; then
    printf '%s\n' \
        "${WARN} \"Nginx Proxy Manager\" - API reachable but initial setup is incomplete"
else
    if [[ -n "$INSTALLED_VERSION" ]]; then
        printf '%s\n' \
            "${OK} \"Nginx Proxy Manager\" - API reachable, version ${INSTALLED_VERSION}"
    else
        printf '%s\n' \
            "${OK} \"Nginx Proxy Manager\" - API reachable"
    fi
fi

#
# ---------------------------------------------------------------------------
# Update check
# ---------------------------------------------------------------------------
#

if [[ "$CHECK_UPDATES" == "yes" ]]; then

    RELEASE_RESPONSE="$(
        curl \
            "${CURL_OPTS[@]}" \
            -H "Accept: application/vnd.github+json" \
            -H "User-Agent: checkmk-npm-local-check" \
            "$NPM_RELEASE_API" \
            2>/dev/null
    )"

    RELEASE_RC=$?

    if [[ $RELEASE_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then

        printf '%s\n' \
            "${UNKNOWN} \"NPM Update\" - Installed ${INSTALLED_VERSION:-unknown}, unable to query GitHub releases"

    elif ! printf '%s' "$RELEASE_RESPONSE" |
        jq -e 'type == "object"' >/dev/null 2>&1
    then

        printf '%s\n' \
            "${UNKNOWN} \"NPM Update\" - GitHub release API returned unexpected data"

    else

        LATEST_VERSION="$(
            printf '%s' "$RELEASE_RESPONSE" |
                jq -r '.tag_name // empty'
        )"

        LATEST_VERSION="${LATEST_VERSION#v}"

        if [[ -z "$LATEST_VERSION" ]]; then

            printf '%s\n' \
                "${UNKNOWN} \"NPM Update\" - Unable to determine latest release"

        elif [[ -z "$INSTALLED_VERSION" ]]; then

            printf '%s\n' \
                "${UNKNOWN} \"NPM Update\" - Latest ${LATEST_VERSION}, installed version unknown"

        else

            NEWEST="$(
                printf '%s\n%s\n' \
                    "$INSTALLED_VERSION" \
                    "$LATEST_VERSION" |
                    sort -V |
                    tail -n 1
            )"

            if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then

                printf '%s\n' \
                    "${OK} \"NPM Update\" - Current: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"

            elif [[ "$NEWEST" == "$LATEST_VERSION" ]]; then

                printf '%s\n' \
                    "${WARN} \"NPM Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"

            else

                printf '%s\n' \
                    "${OK} \"NPM Update\" - Installed ${INSTALLED_VERSION} is newer than latest ${LATEST_VERSION}"

            fi
        fi
    fi
fi

#
# ---------------------------------------------------------------------------
# Authentication
# ---------------------------------------------------------------------------
#

LOGIN_PAYLOAD="$(
    jq -n \
        --arg identity "$NPM_IDENTITY" \
        --arg secret "$NPM_SECRET" \
        '{
            identity: $identity,
            secret: $secret
        }'
)"

TOKEN_RESPONSE="$(
    curl \
        "${CURL_OPTS[@]}" \
        -X POST \
        -H "Content-Type: application/json" \
        -d "$LOGIN_PAYLOAD" \
        "${NPM_URL%/}/api/tokens" \
        2>/dev/null
)"

TOKEN_RC=$?

if [[ $TOKEN_RC -ne 0 || -z "$TOKEN_RESPONSE" ]]; then
    printf '%s\n' \
        "${CRIT} \"NPM Proxy Hosts\" - API reachable but authentication failed"
    exit 0
fi

if ! printf '%s' "$TOKEN_RESPONSE" |
    jq -e 'type == "object"' >/dev/null 2>&1
then
    printf '%s\n' \
        "${UNKNOWN} \"NPM Proxy Hosts\" - Authentication API returned unexpected data"
    exit 0
fi

TOKEN="$(
    printf '%s' "$TOKEN_RESPONSE" |
        jq -r '.token // empty'
)"

TOKEN_EXPIRES="$(
    printf '%s' "$TOKEN_RESPONSE" |
        jq -r '.expires // empty'
)"

if [[ -z "$TOKEN" ]]; then
    printf '%s\n' \
        "${CRIT} \"NPM Proxy Hosts\" - Authentication response did not contain a token"
    exit 0
fi

#
# ---------------------------------------------------------------------------
# Proxy host inventory
# ---------------------------------------------------------------------------
#

HOSTS_RESPONSE="$(
    curl \
        "${CURL_OPTS[@]}" \
        -H "Authorization: Bearer ${TOKEN}" \
        "${NPM_URL%/}/api/nginx/proxy-hosts" \
        2>/dev/null
)"

HOSTS_RC=$?

if [[ $HOSTS_RC -ne 0 || -z "$HOSTS_RESPONSE" ]]; then
    printf '%s\n' \
        "${CRIT} \"NPM Proxy Hosts\" - Unable to retrieve proxy hosts"
    exit 0
fi

if ! printf '%s' "$HOSTS_RESPONSE" |
    jq -e 'type == "array"' >/dev/null 2>&1
then
    printf '%s\n' \
        "${UNKNOWN} \"NPM Proxy Hosts\" - Proxy host API returned unexpected data"
    exit 0
fi

TOTAL_HOSTS="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq 'length'
)"

ENABLED_HOSTS="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq '[.[] | select(.enabled == true)] | length'
)"

DISABLED_HOSTS="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq '[.[] | select(.enabled != true)] | length'
)"

ONLINE_HOSTS="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq '
            [
                .[]
                | select(
                    .enabled == true
                    and
                    (.meta.nginx_online == true)
                )
            ]
            | length
        '
)"

ERROR_HOSTS="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq '
            [
                .[]
                | select(
                    .enabled == true
                    and
                    (
                        .meta.nginx_online != true
                        or
                        .meta.nginx_err != null
                    )
                )
            ]
            | length
        '
)"

#
# ---------------------------------------------------------------------------
# Broken host details
# ---------------------------------------------------------------------------
#

ERROR_LIST="$(
    printf '%s' "$HOSTS_RESPONSE" |
        jq -r '
            .[]
            | select(
                .enabled == true
                and
                (
                    .meta.nginx_online != true
                    or
                    .meta.nginx_err != null
                )
            )
            |
            (
                (.domain_names | join(","))
                +
                " -> "
                +
                (.forward_scheme // "http")
                +
                "://"
                +
                (.forward_host // "unknown")
                +
                ":"
                +
                ((.forward_port // 0) | tostring)
                +
                (
                    if .meta.nginx_err != null
                    then " (" + (.meta.nginx_err | tostring) + ")"
                    else ""
                    end
                )
            )
        ' |
        paste -sd ';' -
)"

STATE=$OK

if [[ "$ERROR_HOSTS" -gt 0 ]]; then
    STATE=$CRIT
fi

PERFDATA="total=${TOTAL_HOSTS};;;0|enabled=${ENABLED_HOSTS};;;0|disabled=${DISABLED_HOSTS};;;0|online=${ONLINE_HOSTS};;;0|errors=${ERROR_HOSTS};;;0"

DETAILS="${ENABLED_HOSTS}/${TOTAL_HOSTS} enabled, ${ONLINE_HOSTS} online"

if [[ "$DISABLED_HOSTS" -gt 0 ]]; then
    DETAILS="${DETAILS}, ${DISABLED_HOSTS} disabled"
fi

if [[ "$ERROR_HOSTS" -gt 0 ]]; then
    DETAILS="${DETAILS}, ${ERROR_HOSTS} with nginx errors"
fi

if [[ -n "$ERROR_LIST" ]]; then
    DETAILS="${DETAILS}: ${ERROR_LIST}"
fi

printf '%s\n' \
    "${STATE} \"NPM Proxy Hosts\" ${PERFDATA} ${DETAILS}"

exit 0